home *** CD-ROM | disk | FTP | other *** search
/ PC Format (PL) 2008 February / PC_Format_022008.iso / Internet / Mozilla Thunderbird wtyczki / lightning-0.7-tb-win.xpi / js / calAlarmService.js < prev    next >
Encoding:
Text File  |  2007-09-23  |  22.0 KB  |  577 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Oracle Corporation code.
  15.  *
  16.  * The Initial Developer of the Original Code is Oracle Corporation
  17.  * Portions created by the Initial Developer are Copyright (C) 2005
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *   Stuart Parmenter <stuart.parmenter@oracle.com>
  22.  *   Joey Minta <jminta@gmail.com>
  23.  *   Daniel Boelzle <daniel.boelzle@sun.com>
  24.  *   Philipp Kewisch <mozilla@kewis.ch>
  25.  *
  26.  * Alternatively, the contents of this file may be used under the terms of
  27.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  28.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29.  * in which case the provisions of the GPL or the LGPL are applicable instead
  30.  * of those above. If you wish to allow use of your version of this file only
  31.  * under the terms of either the GPL or the LGPL, and not to allow others to
  32.  * use your version of this file under the terms of the MPL, indicate your
  33.  * decision by deleting the provisions above and replace them with the notice
  34.  * and other provisions required by the GPL or the LGPL. If you do not delete
  35.  * the provisions above, a recipient may use your version of this file under
  36.  * the terms of any one of the MPL, the GPL or the LGPL.
  37.  *
  38.  * ***** END LICENSE BLOCK ***** */
  39.  
  40. const kHoursBetweenUpdates = 6;
  41.  
  42. function newTimerWithCallback(callback, delay, repeating)
  43. {
  44.     var timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
  45.     
  46.     timer.initWithCallback(callback,
  47.                            delay,
  48.                            (repeating) ? timer.TYPE_REPEATING_PRECISE : timer.TYPE_ONE_SHOT);
  49.     return timer;
  50. }
  51.  
  52. function calAlarmService() {
  53.     this.wrappedJSObject = this;
  54.  
  55.     this.mLoadedCalendars = {};
  56.     this.mTimerLookup = {};
  57.     this.mObservers = new calListenerBag(Components.interfaces.calIAlarmServiceObserver);
  58.  
  59.     this.calendarObserver = {
  60.         alarmService: this,
  61.  
  62.         // calIObserver:
  63.         onStartBatch: function() { },
  64.         onEndBatch: function() { },
  65.         onLoad: function co_onLoad(calendar) {
  66.             // ignore any onLoad events until initial getItems() call of startup has finished:
  67.             if (calendar && this.alarmService.mLoadedCalendars[calendar.id]) {
  68.                 // onLoad signals that changes to the item set are unknown, so purge out all
  69.                 // alarms belonging to the refreshed/loaded calendar:
  70.                 this.alarmService.notifyObservers("onRemoveAlarmsByCalendar", [calendar]);
  71.                 // a refreshed calendar signals that it has been reloaded
  72.                 // (and cannot notify detailed changes), thus reget all alarms of it:
  73.                 this.alarmService.initAlarms([calendar]);
  74.             }
  75.         },
  76.         onAddItem: function(aItem) {
  77.             var occs = [];
  78.             if (aItem.recurrenceInfo) {
  79.                 var start = this.alarmService.mRangeEnd.clone();
  80.                 // We search 1 month in each direction for alarms.  Therefore,
  81.                 // we need to go back 2 months from the end to get this right.
  82.                 start.month -= 2;
  83.                 occs = aItem.recurrenceInfo.getOccurrences(start, this.alarmService.mRangeEnd, 0, {});
  84.             } else {
  85.                 occs = [aItem];
  86.             }
  87.             function hasAlarm(a) {
  88.                 return a.alarmOffset || a.parentItem.alarmOffset;
  89.             }
  90.             occs = occs.filter(hasAlarm);
  91.             for each (var occ in occs) {
  92.                 this.alarmService.addAlarm(occ);
  93.             }
  94.         },
  95.         onModifyItem: function(aNewItem, aOldItem) {
  96.             if (!aNewItem.recurrenceId) {
  97.                 // deleting an occurrence currently calls modifyItem(newParent, *oldOccurrence*)
  98.                 aOldItem = aOldItem.parentItem;
  99.             }
  100.             this.alarmService.removeAlarm(aOldItem);
  101.  
  102.             this.onAddItem(aNewItem);
  103.         },
  104.         onDeleteItem: function(aDeletedItem) {
  105.             this.alarmService.removeAlarm(aDeletedItem);
  106.         },
  107.         onError: function(aErrNo, aMessage) { }
  108.     };
  109.  
  110.  
  111.     this.calendarManagerObserver = {
  112.         alarmService: this,
  113.  
  114.         onCalendarRegistered: function(aCalendar) {
  115.             this.alarmService.observeCalendar(aCalendar);
  116.             // initial refresh of alarms for new calendar:
  117.             this.alarmService.initAlarms([aCalendar]);
  118.         },
  119.         onCalendarUnregistering: function(aCalendar) {
  120.             // XXX todo: we need to think about calendar unregistration;
  121.             // there may still be dangling items (-> alarm dialog),
  122.             // dismissing those alarms may write data...
  123.             this.alarmService.unobserveCalendar(aCalendar);
  124.         },
  125.         onCalendarDeleting: function(aCalendar) {},
  126.         onCalendarPrefChanged: function(aCalendar, aName, aValue, aOldValue) {},
  127.         onCalendarPrefDeleting: function(aCalendar, aName) {}
  128.     };
  129. }
  130.  
  131. var calAlarmServiceClassInfo = {
  132.     getInterfaces: function (count) {
  133.         var ifaces = [
  134.             Components.interfaces.nsISupports,
  135.             Components.interfaces.calIAlarmService,
  136.             Components.interfaces.nsIObserver,
  137.             Components.interfaces.nsIClassInfo
  138.         ];
  139.         count.value = ifaces.length;
  140.         return ifaces;
  141.     },
  142.  
  143.     getHelperForLanguage: function (language) {
  144.         return null;
  145.     },
  146.  
  147.     contractID: "@mozilla.org/calendar/alarm-service;1",
  148.     classDescription: "Calendar Alarm Service",
  149.     classID: Components.ID("{7a9200dd-6a64-4fff-a798-c5802186e2cc}"),
  150.     implementationLanguage: Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT,
  151.     flags: 0
  152. };
  153.  
  154. calAlarmService.prototype = {
  155.     mRangeEnd: null,
  156.     mUpdateTimer: null,
  157.     mStarted: false,
  158.     mTimerLookup: null,
  159.     mObservers: null,
  160.  
  161.     QueryInterface: function cas_QueryInterface(aIID) {
  162.         if (aIID.equals(Components.interfaces.nsIClassInfo))
  163.             return calAlarmServiceClassInfo;
  164.  
  165.         if (!aIID.equals(Components.interfaces.nsISupports) &&
  166.             !aIID.equals(Components.interfaces.calIAlarmService) &&
  167.             !aIID.equals(Components.interfaces.nsIObserver))
  168.         {
  169.             throw Components.results.NS_ERROR_NO_INTERFACE;
  170.         }
  171.  
  172.         return this;
  173.     },
  174.  
  175.  
  176.     /* nsIObserver */
  177.     observe: function cas_observe(subject, topic, data) {
  178.         if (topic == "profile-after-change") {
  179.             this.shutdown();
  180.             this.startup();
  181.         }
  182.         if (topic == "xpcom-shutdown") {
  183.             this.shutdown();
  184.         }
  185.     },
  186.  
  187.     /* calIAlarmService APIs */
  188.     mTimezone: null,
  189.     get timezone() {
  190.         return this.mTimezone;
  191.     },
  192.  
  193.     set timezone(aTimezone) {
  194.         this.mTimezone = aTimezone;
  195.     },
  196.  
  197.     snoozeAlarm: function cas_snoozeAlarm(event, duration) {
  198.         /* modify the event for a new alarm time */
  199.         // Make sure we're working with the parent, otherwise we'll accidentally
  200.         // create an exception
  201.         var newEvent = event.parentItem.clone();
  202.         var alarmTime = jsDateToDateTime((new Date())).getInTimezone("UTC");
  203.  
  204.         // Set the last acknowledged time to now.
  205.         newEvent.alarmLastAck = alarmTime;
  206.  
  207.         alarmTime = alarmTime.clone();
  208.         alarmTime.addDuration(duration);
  209.  
  210.         if (event.parentItem != event) {
  211.             // This is the *really* hard case where we've snoozed a single
  212.             // instance of a recurring event.  We need to not only know that
  213.             // there was a snooze, but also which occurrence was snoozed.  Part
  214.             // of me just wants to create a local db of snoozes here...
  215.             newEvent.setProperty("X-MOZ-SNOOZE-TIME-" + event.recurrenceId.nativeTime,
  216.                                  alarmTime.icalString);
  217.         } else {
  218.             newEvent.setProperty("X-MOZ-SNOOZE-TIME", alarmTime.icalString);
  219.         }
  220.         // calling modifyItem will cause us to get the right callback
  221.         // and update the alarm properly
  222.         newEvent.calendar.modifyItem(newEvent, event.parentItem, null);
  223.     },
  224.  
  225.     dismissAlarm: function cas_dismissAlarm(item) {
  226.         var now = jsDateToDateTime(new Date()).getInTimezone("UTC");
  227.         // We want the parent item, otherwise we're going to accidentally create an
  228.         // exception.  We've relnoted (for 0.1) the slightly odd behavior this can
  229.         // cause if you move an event after dismissing an alarm
  230.         var oldParent = item.parentItem;
  231.         var newParent = oldParent.clone();
  232.         newParent.alarmLastAck = now;
  233.         // Make sure to clear out any snoozes that were here.
  234.         if (item.recurrenceId) {
  235.             newParent.deleteProperty("X-MOZ-SNOOZE-TIME-" + item.recurrenceId.nativeTime);
  236.         } else {
  237.             newParent.deleteProperty("X-MOZ-SNOOZE-TIME");
  238.         }
  239.         newParent.calendar.modifyItem(newParent, oldParent, null);
  240.     },
  241.  
  242.     addObserver: function cas_addObserver(aObserver) {
  243.         this.mObservers.add(aObserver);
  244.     },
  245.  
  246.     removeObserver: function cas_removeObserver(aObserver) {
  247.         this.mObservers.remove(aObserver);
  248.     },
  249.  
  250.     notifyObservers: function cas_notifyObservers(functionName, args) {
  251.         this.mObservers.notify(functionName, args);
  252.     },
  253.  
  254.     hasAlarm: function cas_hasAlarm(aItem) {
  255.         return aItem.alarmOffset || aItem.parentItem.alarmOffset;
  256.     },
  257.  
  258.     startup: function cas_startup() {
  259.         if (this.mStarted)
  260.             return;
  261.  
  262.         if (!this.mTimezone) {
  263.             throw Components.results.NS_ERROR_NOT_INITIALIZED;
  264.         }
  265.  
  266.         LOG("[calAlarmService] starting...");
  267.  
  268.         var observerSvc = Components.classes["@mozilla.org/observer-service;1"]
  269.                           .getService
  270.                           (Components.interfaces.nsIObserverService);
  271.  
  272.         observerSvc.addObserver(this, "profile-after-change", false);
  273.         observerSvc.addObserver(this, "xpcom-shutdown", false);
  274.  
  275.         /* Tell people that we're alive so they can start monitoring alarms.
  276.          */
  277.         this.notifier = Components.classes["@mozilla.org/embedcomp/appstartup-notifier;1"]
  278.                                   .getService(Components.interfaces.nsIObserver);
  279.         var notifier = this.notifier;
  280.         notifier.observe(null, "alarm-service-startup", null);
  281.  
  282.         this.calendarManager = Components.classes["@mozilla.org/calendar/manager;1"]
  283.                                          .getService(Components.interfaces.calICalendarManager);
  284.         var calendarManager = this.calendarManager;
  285.         calendarManager.addObserver(this.calendarManagerObserver);
  286.  
  287.         var calendars = calendarManager.getCalendars({});
  288.         for each(var calendar in calendars) {
  289.             this.observeCalendar(calendar);
  290.         }
  291.  
  292.         /* set up a timer to update alarms every N hours */
  293.         var timerCallback = {
  294.             alarmService: this,
  295.             notify: function timer_notify() {
  296.                 var now = jsDateToDateTime((new Date())).getInTimezone("UTC");
  297.                 var start;
  298.                 if (!this.alarmService.mRangeEnd) {
  299.                     // This is our first search for alarms.  We're going to look for
  300.                     // alarms +/- 1 month from now.  If someone sets an alarm more than
  301.                     // a month ahead of an event, or doesn't start Sunbird/Lightning
  302.                     // for a month, they'll miss some, but that's a slim chance
  303.                     start = now.clone();
  304.                     start.month -= 1;
  305.                 } else {
  306.                     // This is a subsequent search, so we got all the past alarms before
  307.                     start = this.alarmService.mRangeEnd.clone();
  308.                 }
  309.                 var until = now.clone();
  310.                 until.month += 1;
  311.                 
  312.                 // We don't set timers for every future alarm, only those within 6 hours
  313.                 var end = now.clone();
  314.                 end.hour += kHoursBetweenUpdates;
  315.                 this.alarmService.mRangeEnd = end.getInTimezone("UTC");
  316.  
  317.                 this.alarmService.findAlarms(this.alarmService.calendarManager.getCalendars({}),
  318.                                              start, until);
  319.             }
  320.         };
  321.         timerCallback.notify();
  322.  
  323.         this.mUpdateTimer = newTimerWithCallback(timerCallback, kHoursBetweenUpdates * 3600000, true);
  324.  
  325.         this.mStarted = true;
  326.     },
  327.  
  328.     shutdown: function cas_shutdown() {
  329.         /* tell people that we're no longer running */
  330.         var notifier = this.notifier;
  331.         notifier.observe(null, "alarm-service-shutdown", null);
  332.  
  333.         if (this.mUpdateTimer) {
  334.             this.mUpdateTimer.cancel();
  335.             this.mUpdateTimer = null;
  336.         }
  337.         
  338.         var calendarManager = this.calendarManager;
  339.         calendarManager.removeObserver(this.calendarManagerObserver);
  340.  
  341.         for each (var cal in this.mTimerLookup) {
  342.             for each (var itemTimers in cal) {
  343.                 for each (var timer in itemTimers) {
  344.                     if (timer instanceof Components.interfaces.nsITimer) {
  345.                         timer.cancel();
  346.                     }
  347.                 }
  348.             }
  349.         }
  350.         this.mTimerLookup = {};
  351.  
  352.         var calendars = calendarManager.getCalendars({});
  353.         for each(var calendar in calendars) {
  354.             this.unobserveCalendar(calendar);
  355.         }
  356.  
  357.         this.calendarManager = null;
  358.         this.notifier = null;
  359.         this.mRangeEnd = null;
  360.  
  361.         var observerSvc = Components.classes["@mozilla.org/observer-service;1"]
  362.                           .getService
  363.                           (Components.interfaces.nsIObserverService);
  364.  
  365.         observerSvc.removeObserver(this, "profile-after-change");
  366.         observerSvc.removeObserver(this, "xpcom-shutdown");
  367.  
  368.         this.mStarted = false;
  369.     },
  370.  
  371.  
  372.     observeCalendar: function cas_observeCalendar(calendar) {
  373.         calendar.addObserver(this.calendarObserver);
  374.     },
  375.  
  376.     unobserveCalendar: function cas_unobserveCalendar(calendar) {
  377.         calendar.removeObserver(this.calendarObserver);
  378.         this.notifyObservers("onRemoveAlarmsByCalendar", [calendar]);
  379.     },
  380.  
  381.     addAlarm: function cas_addAlarm(aItem) {
  382.         var alarmTime;
  383.         if (aItem.alarmRelated == Components.interfaces.calIItemBase.ALARM_RELATED_START) {
  384.             alarmTime = aItem.startDate || aItem.entryDate || aItem.dueDate;
  385.         } else {
  386.             alarmTime = aItem.endDate || aItem.dueDate || aItem.entryDate;
  387.         }
  388.  
  389.         if (!alarmTime) {
  390.             ASSERT(false, "[calAlarmService] Could not determine alarm time for item: " + aItem.title);
  391.             return;
  392.         }
  393.  
  394.         // Check for snooze
  395.         var snoozeTime;
  396.         if (aItem.parentItem != aItem) {
  397.             snoozeTime = aItem.parentItem.getProperty("X-MOZ-SNOOZE-TIME-"+aItem.recurrenceId.nativeTime)
  398.         } else {
  399.             snoozeTime = aItem.getProperty("X-MOZ-SNOOZE-TIME");
  400.         }
  401.  
  402.         if (snoozeTime && !(snoozeTime instanceof Components.interfaces.calIDateTime)) {
  403.             var time = createDateTime();
  404.             time.icalString = snoozeTime;
  405.             snoozeTime = time;
  406.         }
  407.         LOG("[calAlarmService] snooze time: " + snoozeTime);
  408.         alarmTime = alarmTime.clone();
  409.  
  410.         // Handle all day events.  This is kinda weird, because they don't have
  411.         // a well defined startTime.  We just consider the start/end to be 
  412.         // midnight in the user's timezone.
  413.         if (alarmTime.isDate) {
  414.             alarmTime = alarmTime.getInTimezone(this.mTimezone);
  415.             alarmTime.isDate = false;
  416.         }
  417.  
  418.         var offset = aItem.alarmOffset || aItem.parentItem.alarmOffset;
  419.  
  420.         alarmTime.addDuration(offset);
  421.         alarmTime = alarmTime.getInTimezone("UTC");
  422.         alarmTime = snoozeTime || alarmTime;
  423.         LOG("[calAlarmService] considering alarm for item: " + aItem.title +
  424.             "\n offset: " + offset +
  425.             ", which makes alarm time: " + alarmTime);
  426.         var now = jsDateToDateTime((new Date()));
  427.         if (alarmTime.timezone == "floating") {
  428.             now = now.getInTimezone(calendarDefaultTimezone());
  429.             now.timezone = "floating";
  430.         } else {
  431.             now = now.getInTimezone("UTC");
  432.         }
  433.         LOG("[calAlarmService] now is " + now);
  434.         var callbackObj = {
  435.             alarmService: this,
  436.             item: aItem,
  437.             notify: function(timer) {
  438.                 this.alarmService.alarmFired(this.item);
  439.                 this.alarmService.removeTimers(this.item);
  440.             }
  441.         };
  442.  
  443.         if (alarmTime.compare(now) >= 0) {
  444.             LOG("[calAlarmService] alarm is in the future.");
  445.             // We assume that future alarms haven't been acknowledged
  446.  
  447.             // delay is in msec, so don't forget to multiply
  448.             var timeout = alarmTime.subtractDate(now).inSeconds * 1000;
  449.  
  450.             var timeUntilRefresh = this.mRangeEnd.subtractDate(now).inSeconds * 1000;
  451.             if (timeUntilRefresh < timeout) {
  452.                 LOG("[calAlarmService] alarm is too late.");
  453.                 // we'll get this alarm later.  No sense in keeping an extra timeout
  454.                 return;
  455.             }
  456.  
  457.             this.addTimer(aItem, newTimerWithCallback(callbackObj, timeout, false));
  458.             LOG("[calAlarmService] adding alarm timeout (" + timeout + ") for " + aItem);
  459.         } else {
  460.             var lastAck = aItem.alarmLastAck || aItem.parentItem.alarmLastAck;
  461.             LOG("[calAlarmService] last ack was: " + lastAck);
  462.             // This alarm is in the past.  See if it has been previously ack'd
  463.             if (lastAck && lastAck.compare(alarmTime) >= 0) {
  464.                 LOG("[calAlarmService] " + aItem.title + " - alarm previously ackd.");
  465.                 return;
  466.             } else { // Fire!
  467.                 LOG("[calAlarmService] alarm is in the past and unack'd, firing now!");
  468.                 this.alarmFired(aItem);
  469.             }
  470.         }
  471.     },
  472.  
  473.     removeAlarm: function cas_removeAlarm(aItem) {
  474.         // make sure already fired alarms are purged out of the alarm window:
  475.         this.notifyObservers("onRemoveAlarmsByItem", [aItem]);
  476.         for each (var timer in this.removeTimers(aItem)) {
  477.             if (timer instanceof Components.interfaces.nsITimer) {
  478.                 timer.cancel();
  479.             }
  480.         }
  481.     },
  482.  
  483.     addTimer: function cas_addTimer(aItem, aTimer) {
  484.         var cal = this.mTimerLookup[aItem.calendar.id];
  485.         if (!cal) {
  486.             cal = {};
  487.             this.mTimerLookup[aItem.calendar.id] = cal;
  488.         }
  489.         var itemTimers = cal[aItem.id];
  490.         if (!itemTimers) {
  491.             itemTimers = { mCount: 0 };
  492.             cal[aItem.id] = itemTimers;
  493.         }
  494.         var rid = aItem.recurrenceId;
  495.         itemTimers[rid ? rid.getInTimezone("UTC").icalString : "mTimer"] = aTimer;
  496.         ++itemTimers.mCount;
  497.     },
  498.  
  499.     removeTimers: function cas_removeTimers(aItem) {
  500.         var cal = this.mTimerLookup[aItem.calendar.id];
  501.         if (cal) {
  502.             var itemTimers = cal[aItem.id];
  503.             if (itemTimers) {
  504.                 var rid = aItem.recurrenceId;
  505.                 if (rid) {
  506.                     rid = rid.getInTimezone("UTC").icalString;
  507.                     var timer = itemTimers[rid];
  508.                     if (timer) {
  509.                         delete itemTimers[rid];
  510.                         --itemTimers.mCount;
  511.                         if (itemTimers.mCount == 0) {
  512.                             delete cal[aItem.id];
  513.                         }
  514.                         return { mTimer: timer };
  515.                     }
  516.                 } else {
  517.                     delete cal[aItem.id];
  518.                     return itemTimers;
  519.                 }
  520.             }
  521.         }
  522.         return {};
  523.     },
  524.  
  525.     findAlarms: function cas_findAlarms(calendars, start, until) {
  526.         var getListener = {
  527.             alarmService: this,
  528.             onOperationComplete: function(aCalendar, aStatus, aOperationType, aId, aDetail) {
  529.                 // calendar has been loaded, so until now, onLoad events can be ignored:
  530.                 this.alarmService.mLoadedCalendars[aCalendar.id] = true;
  531.             },
  532.             onGetResult: function(aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
  533.                 for (var i = 0; i < aCount; ++i) {
  534.                     var item = aItems[i];
  535.                     // assure we don't fire alarms twice, handle removed alarms as far as we can:
  536.                     // e.g. we cannot purge removed items from ics files. XXX todo.
  537.                     this.alarmService.removeAlarm(item);
  538.                     if (this.alarmService.hasAlarm(item)) {
  539.                         this.alarmService.addAlarm(item);
  540.                     }
  541.                 }
  542.             }
  543.         };
  544.  
  545.         const calICalendar = Components.interfaces.calICalendar;
  546.         var filter = calICalendar.ITEM_FILTER_COMPLETED_ALL |
  547.                      calICalendar.ITEM_FILTER_CLASS_OCCURRENCES |
  548.                      calICalendar.ITEM_FILTER_TYPE_ALL;
  549.  
  550.         for each(var calendar in calendars) {
  551.             // assuming that suppressAlarms does not change anymore until refresh:
  552.             if (!calendar.suppressAlarms) {
  553.                 calendar.getItems(filter, 0, start, until, getListener);
  554.             }
  555.         }
  556.     },
  557.  
  558.     initAlarms: function cas_refreshAlarms(calendars) {
  559.         // Total refresh similar to startup.  We're going to look for
  560.         // alarms +/- 1 month from now.  If someone sets an alarm more than
  561.         // a month ahead of an event, or doesn't start Sunbird/Lightning
  562.         // for a month, they'll miss some, but that's a slim chance
  563.         var start = jsDateToDateTime((new Date())).getInTimezone("UTC");
  564.         var until = start.clone();
  565.         start.month -= 1;
  566.         until.month += 1;
  567.         this.findAlarms(calendars, start, until);
  568.     },
  569.  
  570.     alarmFired: function cas_alarmFired(event) {
  571.         if (event.calendar.suppressAlarms)
  572.             return;
  573.  
  574.         this.notifyObservers("onAlarm", [event]);
  575.     }
  576. };
  577.